// (C) 2022 Intel Corporation. All rights reserved.
// Your use of Intel Corporation's design tools, logic functions and other 
// software and tools, and its AMPP partner logic functions, and any output 
// files from any of the foregoing (including device programming or simulation 
// files), and any associated documentation or information are expressly subject 
// to the terms and conditions of the Intel Program License Subscription 
// Agreement, Intel FPGA IP License Agreement, or other applicable 
// license agreement, including, without limitation, that your use is for the 
// sole purpose of programming logic devices manufactured by Intel and sold by 
// Intel or its authorized distributors.  Please refer to the applicable 
// agreement for further details.

`include "mainfpga_version.vh"
module Pmode #(
    parameter CLOCK_PERIOD_NS       = 20        // period of the input 'clock', in picoseconds (default 20,000 ps = 50 MHz)
 
) (
    input  wire                            CLOCK_50M,         // master clock for this block
    input  wire                            RESET_N,           // master reset, must be de-asserted synchronously with clock
    
    input  wire                            POD_PRESENT_N,
    input  wire                            CPU_PWRGD,
    input  wire                            S0_PWR_OK,
    input  wire                            PLTRST,
    input  wire                            Silicon_GLB_RST,
    
    output wire                            PMODE

);

//////////////////////////////////////////////////////////////////////////////////
// Local Parameters
//////////////////////////////////////////////////////////////////////////////////
localparam    LOW                             = 1'b0;
localparam    HIGH                            = 1'b1;

`ifdef SIMULATION
localparam int DELAY_PERIOD               = 10_000; // 10 us
`else
localparam int DELAY_PERIOD               = 10_000_000; // 10 ms
`endif

localparam int DELAY_CLOCK_CYCLES             = DELAY_PERIOD / CLOCK_PERIOD_NS;     // clock cycles of delay
localparam int COUNTER_BIT_WIDTH              = $clog2(DELAY_CLOCK_CYCLES) + 1;             // counter will count from 0 to DELAY_CLOCK_CYCLES.


////////////////////////////////////////////////////////////////////////////
//regs
////////////////////////////////////////////////////////////////////////////

reg PMODE_reg;
reg [COUNTER_BIT_WIDTH - 1 : 0] counter;

reg POD_PRESENT_N_ff;
reg POD_PRESENT_N_2ff;
reg POD_PRESENT_N_3ff;
reg POD_PRESENT_N_4ff;

reg [3:0] PMODE_status_bits_ff;
reg [3:0] PMODE_status_bits_2ff;
reg [3:0] PMODE_status_bits_3ff;
reg [3:0] PMODE_status_bits_4ff;

reg PMODE_status_bits_changed_reg;
reg [7:0] Pmode_status_byte;
reg [7:0] Pmode_status_byte_2ff;
reg [3:0] PMODE_delay_counter;
reg [3:0] PMODE_bits_counter;
reg TRANSMIT_A_PMODE_PACKET_enable;
reg counter_enable;
reg wait_some_time_done;
reg TRANSMIT_A_PMODE_PACKET_done;


////////////////////////////////////////////////////////////////////////////
//Wires
///////////////////////////////////////////////////////////////////////////
wire negedge_POD_PRESENT_N;
wire PMODE_status_bits_changed;


/////////////////////////////////////////////////////////////////////////
//continuous assignment
/////////////////////////////////////////////////////////////////////////
assign negedge_POD_PRESENT_N = POD_PRESENT_N_4ff & (~POD_PRESENT_N_3ff);
assign PMODE_status_bits_changed = | (PMODE_status_bits_4ff ^ PMODE_status_bits_3ff);
assign PMODE = PMODE_reg;


    ///////////////////////////////////////
    // Track the 'phase' of the PMODE module
    ///////////////////////////////////////
    
    
    enum {
                INITIAL,            
                DETECT_DEBUG_TOOL_CONNECTED,                                 
                CONFIRM_DEBUG_TOOL_CONNECTED,                                
                SKIP_DEBUG_TOOL_DISCONNECTING_PERIOD,                              
                DETECT_PMODE_STATUS_CHANGED,                 
                SAMPLE_PMODE_STATUS,                            
                TRANSMIT_A_PMODE_PACKET,                
                CONFIRM_PMODE_STATUS_CHANGED_DURING_PACKET_TRANSMISSION 
    } pmode_state;                // current state of the PMODE module
    
    enum {
        INITIAL_CNT                            ,
        COUNTING        
                    
    } counter_state;                // current state of the counter state machine


    enum {
       
                INITIAL_T,                            
                KEEP_A_PMODE_PERIOD_IDLE,                
                TRANSMITTING
                             
    } transmit_a_pmode_packet_state;                // current state of the transmitting a Pmome Packet state machine


//Clock Domain Crossing
//remember to do CDC   
//Pipeline of PMODE_STATUS
   always_ff @(posedge CLOCK_50M) begin


          PMODE_status_bits_ff  <= {CPU_PWRGD,S0_PWR_OK,PLTRST,Silicon_GLB_RST};
          PMODE_status_bits_2ff <= PMODE_status_bits_ff;     
       
   end


//Clock Domain Crossing
//Pipeline of POD_PRESENT_N
   always_ff @(posedge CLOCK_50M) begin

          POD_PRESENT_N_ff  <= POD_PRESENT_N;
          POD_PRESENT_N_2ff <= POD_PRESENT_N_ff;
       
   end



//Pipeline of POD_PRESENT_N
   always_ff @(posedge CLOCK_50M) begin
       
         
          POD_PRESENT_N_3ff <= POD_PRESENT_N_2ff;
		  POD_PRESENT_N_4ff <= POD_PRESENT_N_3ff;
       
   end
   
   
  
//Pipeline of PMODE_STATUS
   always_ff @(posedge CLOCK_50M) begin
   
          PMODE_status_bits_3ff <= PMODE_status_bits_2ff;  
          PMODE_status_bits_4ff <= PMODE_status_bits_3ff;  		  
       
   end
   
// Main state machine of Pmode Module   
   always_ff @(posedge CLOCK_50M or negedge RESET_N) begin
        if (~RESET_N) begin
            pmode_state                   <= INITIAL;
            counter_enable <= LOW;
            PMODE_status_bits_changed_reg <= LOW;
            Pmode_status_byte             <= 8'd0;
            
            TRANSMIT_A_PMODE_PACKET_enable      <= LOW;
        end
        else begin
        
            case ( pmode_state )
                // This is the reset state.
                INITIAL: begin
                    counter_enable <= LOW;
                    TRANSMIT_A_PMODE_PACKET_enable <= LOW;
                    PMODE_status_bits_changed_reg <= LOW;
                    if (POD_PRESENT_N_2ff == HIGH) begin                
                        pmode_state <= DETECT_DEBUG_TOOL_CONNECTED;    
                    end else if (POD_PRESENT_N_2ff == LOW) begin
                        pmode_state <= DETECT_PMODE_STATUS_CHANGED;
                					
                    end                   
                end
                
                DETECT_DEBUG_TOOL_CONNECTED : begin     //detect hot plugging 
                  if (negedge_POD_PRESENT_N) begin
                     counter_enable <= HIGH;
                     pmode_state    <= CONFIRM_DEBUG_TOOL_CONNECTED;
                  end
                            
                end
                
                CONFIRM_DEBUG_TOOL_CONNECTED : begin
                  counter_enable <= LOW;
                  if (wait_some_time_done && ~POD_PRESENT_N_2ff) begin   //The debug tool is really connected.
                      pmode_state <= SAMPLE_PMODE_STATUS;
					
                  end else if (wait_some_time_done && POD_PRESENT_N_2ff) begin  //The debug tool is not really connected
                      pmode_state <= INITIAL;
                  end
                end
                
				
                SKIP_DEBUG_TOOL_DISCONNECTING_PERIOD : begin
                     counter_enable <= LOW;                    
                     if (wait_some_time_done) begin   //The debug tool is really disconnected.
                       pmode_state <= INITIAL;  
                     end					   
                end 
                
                
                DETECT_PMODE_STATUS_CHANGED : begin
                   if (POD_PRESENT_N_2ff) begin
				      counter_enable <= HIGH;  
                      pmode_state <= SKIP_DEBUG_TOOL_DISCONNECTING_PERIOD;
               
                   end
                   else if (PMODE_status_bits_changed) begin
                      pmode_state <= SAMPLE_PMODE_STATUS;
                   end 
                end
                
                SAMPLE_PMODE_STATUS: begin
                    Pmode_status_byte <= {~PMODE_status_bits_2ff[0],3'b000,~PMODE_status_bits_2ff[1],PMODE_status_bits_2ff[2],1'b0,PMODE_status_bits_2ff[3]};
                    PMODE_status_bits_changed_reg <= LOW;      //clear PMODE_status_bits_changed_reg and prepare for storage whether the Pmode status is changed or not during a Pmode packet transmission
                    pmode_state <= TRANSMIT_A_PMODE_PACKET;
                    TRANSMIT_A_PMODE_PACKET_enable <= HIGH;
                end
                
                
                TRANSMIT_A_PMODE_PACKET: begin
                     if (PMODE_status_bits_changed) begin
                         PMODE_status_bits_changed_reg <= HIGH;
                     end 
                         
                     TRANSMIT_A_PMODE_PACKET_enable <= LOW;
                     if (TRANSMIT_A_PMODE_PACKET_done) begin
                        pmode_state <= CONFIRM_PMODE_STATUS_CHANGED_DURING_PACKET_TRANSMISSION;
                        
                     end
                end
                
                CONFIRM_PMODE_STATUS_CHANGED_DURING_PACKET_TRANSMISSION : begin
                   if (PMODE_status_bits_changed_reg) begin
                       pmode_state <= SAMPLE_PMODE_STATUS;   
                   end else begin
                       pmode_state <= DETECT_PMODE_STATUS_CHANGED;   
                   end
                end
                    
                default: begin
                   pmode_state                 <= INITIAL;            
                end
            endcase                         
        end     
    end
    
    //counting state machine
    always_ff @(posedge CLOCK_50M or negedge RESET_N) begin
         if (~RESET_N) begin
            counter <= {COUNTER_BIT_WIDTH{1'b0}};
            counter_state <= INITIAL_CNT;
            wait_some_time_done <= LOW;
         end else begin
              case ( counter_state )
                    // This is the reset state.
                    INITIAL_CNT: begin
                        wait_some_time_done <= LOW;
                        counter <= {COUNTER_BIT_WIDTH{1'b0}};
                        if (counter_enable == HIGH) begin                  
                            counter_state <= COUNTING;
                        end                         
                    end
                    
                    COUNTING : begin
                      counter <= counter + 1'b1;
                      if (counter == DELAY_CLOCK_CYCLES) begin 
                         wait_some_time_done <= HIGH;
                         counter_state   <= INITIAL_CNT;
                      end            
                    end
                        
                    default : begin
                       counter_state  <= INITIAL_CNT;
                    end
              endcase                         
         end
    end
    
    
    //Transmit a Packet state Machine
    always_ff @(posedge CLOCK_50M or negedge RESET_N) begin
         if (~RESET_N) begin
        
            PMODE_delay_counter           <= 4'd0;
            PMODE_bits_counter            <= 4'd0;
            Pmode_status_byte_2ff         <= 8'd0;
            transmit_a_pmode_packet_state <= INITIAL_T;
            TRANSMIT_A_PMODE_PACKET_done  <= LOW;
            PMODE_reg                     <= HIGH;      //default value is HIGH, and it means IDLE in Serial Pmode Protocal
            
         end else begin
              case ( transmit_a_pmode_packet_state )
                    // This is the reset state.
                    INITIAL_T: begin
                        TRANSMIT_A_PMODE_PACKET_done <= LOW;
                        PMODE_delay_counter           <= 4'd0;
                        PMODE_bits_counter            <= 4'd0;
                        if (TRANSMIT_A_PMODE_PACKET_enable == HIGH) begin                  
                            transmit_a_pmode_packet_state <= KEEP_A_PMODE_PERIOD_IDLE;
                            Pmode_status_byte_2ff <= Pmode_status_byte;        
                        end                         
                    end
                                    
                    KEEP_A_PMODE_PERIOD_IDLE : begin        //At least keep a Period of IDLE of PMODE_reg
                         PMODE_reg <= HIGH;
                         if (PMODE_delay_counter < 4'd4) begin 
                             PMODE_delay_counter <= PMODE_delay_counter + 1'b1;
                         end else begin
                             PMODE_delay_counter <= 4'd0;
                             transmit_a_pmode_packet_state <= TRANSMITTING;
                         end                                                     
                    end
                    
                    TRANSMITTING : begin
                      
                      PMODE_delay_counter <= PMODE_delay_counter + 1'b1;
                      
                      if (PMODE_bits_counter <= 4'd7) begin
                      
                          if (PMODE_delay_counter == 4'd0) begin
                             PMODE_reg <= LOW;
                          end else if (PMODE_delay_counter == 1) begin
                             PMODE_reg <= Pmode_status_byte_2ff[0];
                          
                          end else if (PMODE_delay_counter == 3) begin
                             PMODE_reg <= HIGH;
                             Pmode_status_byte_2ff <= {1'b0,Pmode_status_byte_2ff[7:1]};
                             PMODE_delay_counter   <= 4'd0;
                             PMODE_bits_counter    <=  PMODE_bits_counter + 1'b1;
                          end
                      end else begin
                            TRANSMIT_A_PMODE_PACKET_done  <= HIGH;
                            transmit_a_pmode_packet_state <= INITIAL_T;
                      end
                    end
                        
                    default : begin
                       transmit_a_pmode_packet_state  <= INITIAL_T;
                       PMODE_reg                      <= HIGH;  
                    end
                endcase                         
                
        end
    end

endmodule
